package org.marverse.scripting

import ScriptingUtil._

/**
 * Representation of a tuple. Tuples are finite sequences of values.
 * @see TupleType
 */
final case class TupleValue (val components: Seq[_]) {
  override def toString = components.mkString("(", ", ", ")")
}

object TupleValue {
  def apply(): TupleValue = 
    TupleValue(Seq.empty)
  def apply(v1: Any): TupleValue =
    TupleValue(Seq(v1))
  def apply(v1: Any, v2: Any): TupleValue =
    TupleValue(Seq(v1, v2))
  def apply(v1: Any, v2: Any, v3: Any): TupleValue =
    TupleValue(Seq(v1, v2, v3))
}

/**
 * <p>Representation of a tuple type. Tuple types are finite sequences of named types, 
 * which in turn, define the type of the respective name and type of components of tuples.</p>
 * <p>Defines the following members:
 * <ul>
 * <li><code>length: number</code> - the number of components</li>
 * <li><code>_i: T<sub>i</sub></code> for every component, where
 * <emph>i</emph> is the index (starting with zero) and <emph>T<sub>i</sub></emph> is the type of the component</li>
 * </ul>
 * @see TupleValue
 */
final case class TupleType(val componentTypes: Seq[_ <: VariableDeclaration]) extends Type {
  // TODO: update toString to a better/correct representation
  override def toString = componentTypes.mkString("tuple[", ", ", "]") 
  override def memberDeclarations = Seq(
      ConstMember('length, TupleType.this, NumberType, componentTypes.length)
    ) ++ (componentTypes.toList.zipWithIndex map (v => 
      ValueMember(Symbol("_" + v._2.toString), TupleType.this, v._1.variableType, _.asInstanceOf[TupleValue].components(v._2)) 
    ))

  override def isAssignableFrom(that: Type, ctx: MutableTypedMap[TypedKey, Any]) = 
    that match {
      case tt: TupleType =>
        (tt.componentTypes.size == componentTypes.size) &&
        componentTypes.elements.zip(tt.componentTypes.elements).forall(p => 
          p._1.name == p._2.name &&
          p._1.variableType.isAssignableFrom(p._2.variableType, ctx))
      case _ => super.isAssignableFrom(that, ctx)
    }
  
  override def isInstance(value: Any) = 
    value.isInstanceOf[TupleValue] && value.asInstanceOf[TupleValue].components.size == componentTypes.size &&
      (componentTypes.elements.zip(value.asInstanceOf[TupleValue].components.elements) forall { v => v._1.variableType.isInstance(v._2) })
}

object TupleType {
  /**
   * Returns a tuple type with zero component types.
   */
  def apply(): TupleType = TupleType(Seq.empty)
  /**
   * Returns a tuple type with one component type.
   */
  def apply[T1 <% Type](n1: Symbol, t1: T1): TupleType = 
    TupleType(Seq(UncheckedVariableExpr(n1, t1).declaration))
  /**
   * Returns a tuple type with two component types.
   */
  def apply[T1 <% Type, T2 <% Type](n1: Symbol, t1: T1, 
                                    n2: Symbol, t2: T2): TupleType = 
    TupleType(Seq(UncheckedVariableExpr(n1, t1).declaration, 
                  UncheckedVariableExpr(n2, t2).declaration))
  /**
   * Returns a tuple type with three component types.
   */
  def apply[T1 <% Type, T2 <% Type, T3 <% Type](n1: Symbol, t1: T1, 
                                                n2: Symbol, t2: T2, 
                                                n3: Symbol, t3: T3): TupleType = 
    TupleType(Seq(UncheckedVariableExpr(n1, t1).declaration, 
                  UncheckedVariableExpr(n2, t2).declaration, 
                  UncheckedVariableExpr(n3, t3).declaration))
}